/**
 * @file       main.c
 * @author     mz8023yt
 * @email      mz8023yt@163.com
 * @version    V1.0
 * @date       2022-03-13
 * @brief      st7789 lcd test
 */

#include "colorbar.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "st7789.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>

/* LCD1 */
#define ST7789_CLK_PIN  2
#define ST7789_MOSI_PIN 3
#define ST7789_CS_PIN   10
#define ST7789_RST_PIN  6
#define ST7789_DC_PIN   7
#define ST7789_BL_PIN   11

/* LCD2 */
#define ST7789_CS2_PIN 5
#define ST7789_DC2_PIN 4

static spi_device_handle_t spi_lcd;
static spi_device_handle_t spi_lcd2;

void esp32c3_spi_write(uint8_t* bytes, uint32_t len)
{
	uint32_t section = 4096;
	spi_transaction_t t;

	if (!len) {
		return;
	}

	while (len > section) {
		memset(&t, 0, sizeof(t));                 // Zero out the transaction
		t.length = 8 * section;                   // Command is 8 bits
		t.tx_buffer = bytes;                      // The data is the cmd itself
		spi_device_polling_transmit(spi_lcd, &t); // Transmit!
		bytes += section;
		len -= section;
	}

	memset(&t, 0, sizeof(t));                 // Zero out the transaction
	t.length = 8 * len;                       // Command is 8 bits
	t.tx_buffer = bytes;                      // The data is the cmd itself
	spi_device_polling_transmit(spi_lcd, &t); // Transmit!
}

void esp32c3_spi_write2(uint8_t* bytes, uint32_t len)
{
	uint32_t section = 4096;
	spi_transaction_t t;

	if (!len) {
		return;
	}

	while (len > section) {
		memset(&t, 0, sizeof(t));                  // Zero out the transaction
		t.length = 8 * section;                    // Command is 8 bits
		t.tx_buffer = bytes;                       // The data is the cmd itself
		spi_device_polling_transmit(spi_lcd2, &t); // Transmit!
		bytes += section;
		len -= section;
	}

	memset(&t, 0, sizeof(t));                  // Zero out the transaction
	t.length = 8 * len;                        // Command is 8 bits
	t.tx_buffer = bytes;                       // The data is the cmd itself
	spi_device_polling_transmit(spi_lcd2, &t); // Transmit!
}

void esp32c3_rst_pin(int val)
{
	gpio_set_level(ST7789_RST_PIN, val);
}

void esp32c3_dc_pin(int val)
{
	gpio_set_level(ST7789_DC_PIN, val);
}

void esp32c3_dc2_pin(int val)
{
	gpio_set_level(ST7789_DC2_PIN, val);
}

void esp32c3_bl_pin(int val)
{
	gpio_set_level(ST7789_BL_PIN, val);
}

void esp32c3_msleep(int ms)
{
	usleep(1000 * ms);
}

void board_init()
{
	/* gpio */
	gpio_reset_pin(ST7789_RST_PIN);
	gpio_set_direction(ST7789_RST_PIN, GPIO_MODE_OUTPUT);

	gpio_reset_pin(ST7789_DC_PIN);
	gpio_set_direction(ST7789_DC_PIN, GPIO_MODE_OUTPUT);
	gpio_reset_pin(ST7789_DC2_PIN);
	gpio_set_direction(ST7789_DC2_PIN, GPIO_MODE_OUTPUT);

	gpio_reset_pin(ST7789_BL_PIN);
	gpio_set_direction(ST7789_BL_PIN, GPIO_MODE_OUTPUT);

	spi_bus_config_t buscfg = {
		.miso_io_num = -1,
		.mosi_io_num = ST7789_MOSI_PIN,
		.sclk_io_num = ST7789_CLK_PIN,
		.quadwp_io_num = -1,
		.quadhd_io_num = -1,
		.max_transfer_sz = 4096,
	};

	spi_device_interface_config_t devcfg = {
		.clock_speed_hz = 80000000,    // SPI clock
		.mode = 3,                     // SPI mode
		.spics_io_num = ST7789_CS_PIN, // CS pin
		.queue_size = 7,               // We want to be able to queue 7 transactions at a time
	};

	spi_device_interface_config_t devcfg2 = {
		.clock_speed_hz = 80000000,     // SPI clock
		.mode = 3,                      // SPI mode
		.spics_io_num = ST7789_CS2_PIN, // CS pin
		.queue_size = 7,                // We want to be able to queue 7 transactions at a time
	};

	spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
	spi_bus_add_device(SPI2_HOST, &devcfg, &spi_lcd);
	spi_bus_add_device(SPI2_HOST, &devcfg2, &spi_lcd2);
}

void app_main(void)
{
	struct st7789 lcd;
	struct st7789 lcd2;
	uint8_t* buf = NULL;

	board_init();

	/* ================================================================== */
	/* set lcd param */
	lcd.cfg.fix.width = 240;
	lcd.cfg.fix.height = 240;
	lcd.cfg.fix.xshift = 0;
	lcd.cfg.fix.yshift = 0;
	lcd.cfg.fix.bits = 8;
	lcd.cfg.var.orien = 0;

	/* register the operation */
	lcd.ops.reset = esp32c3_rst_pin;
	lcd.ops.datacmd = esp32c3_dc_pin;
	lcd.ops.backlight = esp32c3_bl_pin;
	lcd.ops.msleep = esp32c3_msleep;
	lcd.ops.write = esp32c3_spi_write;

	/* reset sequence */
	/* the spec requires at least 10us */
	lcd.ops.reset(LOW);
	lcd.ops.msleep(100);

	/* the spec requires a maximum wait of 120ms */
	lcd.ops.reset(HIGH);
	lcd.ops.msleep(120);

	/* display panel init */
	st7789_init(&lcd);

	/**********************************************************************/

	/* set lcd param */
	lcd2.cfg.fix.width = 240;
	lcd2.cfg.fix.height = 240;
	lcd2.cfg.fix.xshift = 0;
	lcd2.cfg.fix.yshift = 0;
	lcd2.cfg.fix.bits = 8;
	lcd2.cfg.var.orien = 0;

	/* register the operation */
	lcd2.ops.reset = esp32c3_rst_pin;
	lcd2.ops.datacmd = esp32c3_dc2_pin;
	lcd2.ops.backlight = esp32c3_bl_pin;
	lcd2.ops.msleep = esp32c3_msleep;
	lcd2.ops.write = esp32c3_spi_write2;

	/* display panel init */
	st7789_init(&lcd2);

	/**********************************************************************/

	/* display colorbar */
	buf = malloc(lcd.cfg.fix.width * lcd.cfg.fix.height * 2);
	colorbar_generate(buf, lcd.cfg.fix.width, lcd.cfg.fix.height);
	st7789_rect_fill(&lcd, 0, 0, lcd.cfg.fix.width - 1, lcd.cfg.fix.height - 1, (uint16_t*)buf);
	st7789_rect_fill(&lcd2, 0, 0, lcd.cfg.fix.width - 1, lcd.cfg.fix.height - 1, (uint16_t*)buf);

	while (1) {
		vTaskDelay(1000 / portTICK_PERIOD_MS);
	}
}
